/**
 * Mock Request Listener
 * @author Wangtd
 */
const url = require('url');
const fs = require('fs');
const path = require('path');
const moment = require('moment-timezone');

const XML = require('./xml.js');
const JSON5 = require('./json5.js');
const Mock = require('./mock.js');

function isFileMatched(fileName, method, pathName) {
    var pathreg = new RegExp('^' + pathName + '(\\.'+ method + ')?$');
    if (!pathreg.test(fileName)) {
        pathreg = new RegExp('^\\{[0-9A-Za-z_-]+\\}(\\.' + method + ')?$');
        return pathreg.test(fileName);
    }
    return true;
}

function findRestApiFile(parent, method, paths, index) {
    var fileNames = fs.readdirSync(parent);
    for (var i in fileNames) {
        var fileName = fileNames[i];
        var subFile = fs.statSync(path.join(parent, fileName));
        if (index < paths.length - 1) {
            // directory
            if (subFile.isDirectory() && isFileMatched(fileName, method, paths[index])) {
                var restFile = findRestApiFile(path.join(parent, fileName), method, paths, index + 1);
                if (restFile) {
                    return restFile;
                }
            }
        } else if (index == paths.length - 1) {
            // file
            if (subFile.isFile()) {
                var baseName = path.basename(fileName, path.extname(fileName));
                if (isFileMatched(baseName, method, paths[index])) {
                    return path.join(parent, fileName);
                }
            }
        } else {
            break;
        }
    }
    return null;
}

function Mocked() {}
Mocked.prototype.mock = function(basedir) {
    return function(request, response, next) {
        var mocked = request.headers['x-mocked'];
        var urlObj = url.parse(request.url, true);
        var pathName = urlObj.pathname;
        var method = request.method.toLowerCase();
        var isOriginal = urlObj.query && urlObj.query['origin'];

        pathName = decodeURI(pathName);

        var extName = path.extname(pathName);

        if (extName === '') {
            if (pathName.charAt(pathName.length - 1) !== '/') {
                var baseName = path.basename(pathName);
                if (baseName.indexOf('.') === 0) {
                    // when file name is empty
                    extName = baseName;
                } else {
                    // default extension
                    extName = 'json|xml';
                }
            } else if (mocked) {
                pathName += '@index';
                extName = 'json|xml';
            }
        }

        var filePath = null,
            exists = true;

        if (extName === '') {
            // folder
            filePath = path.join(basedir, pathName);
            exists = fs.existsSync(filePath);
            if (exists) {
                try {
                    var indexName = pathName;
                    if (pathName !== '/') {
                        indexName = pathName.substring(0, pathName.lastIndexOf('/'));
                    }

                    var html = '<!DOCTYPE html>';
                    html += '<html lang="zh-CN">';
                    html += '<head>';
                    html += '<meta charset="UTF-8">';
                    html += '<title>Index of ' + indexName + '</title>';
                    // font style
                    html += '<style>body{font-family:"Trebuchet MS",verdana,lucida,arial,helvetica,sans-serif;}</style>';
                    html += '</head>';

                    html += '<body>';
                    html += '<h1>Index of ' + indexName + '</h1>';
                    html += '<table cellspacing="10">';
                    html += '<tr><th align="left">Name</th><th>Last Modified</th><th>Size</th><th>Description</th></tr>';
                    html += '<tr><td><a href="../">Parent Directory</a></td></tr>';

                    // list files
                    var filedir = filePath.substring(0, filePath.lastIndexOf(path.sep));
                    var files = fs.readdirSync(filedir);
                    var timezone = moment.tz.guess();
                    for (var i in files) {
                        var filename = files[i];
                        var link = filename, size = '';
                        var stat = fs.statSync(path.join(filedir, filename));
                        if(stat.isDirectory()) {
                            if (filename.indexOf('.') == 0) {// .git
                                continue;
                            }
                            link += '/';
                        } else {
                            size = stat.size;
                        }
                        html += '<tr><td><a href="' + link + '">' + link + '</a></td><td>'
                             + moment(stat.mtime).tz(timezone).format('ddd MMM DD HH:mm:ss z YYYY') + '</td><td align="right">'
                             + size +'</td></tr>';
                    }

                    html += '</table>';
                    html += '</body>';
                    html += '</html>';

                    response.writeHead(200, { 'content-type': 'text/html' });
                    response.end(html);
                } catch (e) {
                    console.error(e);
                    response.writeHead(500, { 'content-type': 'text/html' });
                    response.end('<h1>500 Server Error</h1><pre>' + e + '</pre>');
                }
            }
        } else {
            // file
            if (extName == 'json|xml') {
                var extNames = [
                    '.json', '.' + method + '.json', // json
                    '.xml',  '.' + method + '.xml'   // xml
                ];
                for (var i = 0; i < extNames.length; i++) {
                    extName = extNames[i];
                    filePath = path.join(basedir, pathName + extName);
                    exists = fs.existsSync(filePath);
                    if (exists) {
                        break;
                    }
                }
                // rest api
                if (!exists) {
                    filePath = findRestApiFile(basedir, method, pathName.substring(1).split(/\//g), 0);
                    exists = filePath ? true : false;
                    if (exists) {
                        extName = path.extname(filePath);
                    }
                }
            } else {
                filePath = path.join(basedir, pathName);
                exists = fs.existsSync(filePath);
            }
            if (exists) {
                try {
                    var status = 200;
                    if (urlObj.query && urlObj.query['status']) {
                        status = parseInt(urlObj.query['status']);
                    }

                    var content = fs.readFileSync(filePath, 'utf-8'),
                        modified = fs.statSync(filePath).mtime.getTime();
                    if (isOriginal || extName.lastIndexOf('.json') == -1 && extName.lastIndexOf('.xml') == -1) {
                        response.writeHead(status, {
                            'content-type': 'text/plain;charset=utf-8',
                            'x-modified': modified
                        });
                    } else if (extName.lastIndexOf('.json') != -1) {
                        content = JSON.stringify(Mock.mock(JSON5.parse(content)));
                        response.writeHead(status, {
                            'content-type': 'application/json;charset=utf-8',
                            'x-modified': modified
                        });
                    } else if (extName.lastIndexOf('.xml') != -1) {
                        content = XML.format(content, { method: 'xmlmin' }).trim();
                        response.writeHead(status, {
                            'content-type': 'application/xml;charset=utf-8',
                            'x-modified': modified
                        });
                    }
                    response.end(content);
                } catch (e) {
                    console.error(e);
                    response.writeHead(500, { 'content-type': 'text/html' });
                    response.end('<h1>500 Server Error</h1><pre>' + e + '</pre>');
                }
            }
        }

        if (!exists) {
            response.writeHead(404, { 'content-type': 'text/html' });
            response.end('<h1>404 Not Found</h1>');
        }
    };
}

module.exports = new Mocked();
